package com.shxzz.shortlink.project.service.impl;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.text.CharSequenceUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.shxzz.shortlink.project.dao.entity.*;
import com.shxzz.shortlink.project.dao.mapper.*;
import com.shxzz.shortlink.project.dto.req.GroupLinkAccessRecordReqDTO;
import com.shxzz.shortlink.project.dto.req.GroupLinkStatusReqDTO;
import com.shxzz.shortlink.project.dto.req.LinkAccessRecordReqDTO;
import com.shxzz.shortlink.project.dto.req.LinkStatusReqDTO;
import com.shxzz.shortlink.project.dto.resp.*;
import com.shxzz.shortlink.project.service.LinkStatusService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author shxzz
 * @create 2023-12-27-15:52
 * @description 短链接监控信息接口实现类
 */
@Service
@RequiredArgsConstructor
public class LinkStatusServiceImpl implements LinkStatusService {

    private final LinkLocaleStatusMapper linkLocaleStatusMapper;

    private final LinkDeviceStatusMapper linkDeviceStatusMapper;

    private final LinkBrowserStatusMapper linkBrowserStatusMapper;

    private final LinkNetworkStatusMapper linkNetworkStatusMapper;

    private final LinkOsStatusMapper linkOsStatusMapper;

    private final LinkAccessLogMapper linkAccessLogMapper;

    private final LinkAccessStatusMapper linkAccessStatusMapper;

    @Override
    public LinkStatusRespDTO oneLinkStatus(LinkStatusReqDTO requireParam) {

        // 基础访问信息
        LinkAccessStatusDO linkAccessPvUvUip = linkAccessLogMapper.selectTotalPvUvUip(requireParam);
        Integer pv = linkAccessPvUvUip.getPv();
        Integer uv = linkAccessPvUvUip.getUv();
        Integer uip = linkAccessPvUvUip.getUip();
        if (pv == null || pv.equals(0)) {
            return null;
        }

        // 每日基础访问信息
        List<LinkAccessStatusDO> linkStatsDailyList = linkAccessStatusMapper.selectDailyList(requireParam);
        List<String> rangeDateList = DateUtil.rangeToList(DateUtil.parse(requireParam.getStartDate()), DateUtil.parse(requireParam.getEndDate()), DateField.DAY_OF_MONTH)
                .stream()
                .map(DateUtil::formatDate)
                .toList();
        List<LinkStatsAccessDailyRespDTO> daily = new ArrayList<>(rangeDateList.size());
        rangeDateList.forEach(date -> linkStatsDailyList.stream()
                .filter(linkStatusDO -> CharSequenceUtil.equals(linkStatusDO.getAccessDate().toString(), date))
                .findFirst()
                .ifPresentOrElse(linkStatusDO -> {
                            LinkStatsAccessDailyRespDTO linkStatsAccessDailyRespDTO = LinkStatsAccessDailyRespDTO.builder()
                                    .date(date)
                                    .pv(linkStatusDO.getPv())
                                    .uv(linkStatusDO.getUv())
                                    .uip(linkStatusDO.getUip())
                                    .build();
                            daily.add(linkStatsAccessDailyRespDTO);
                        }, () -> {
                            LinkStatsAccessDailyRespDTO linkStatsAccessDailyRespDTO = LinkStatsAccessDailyRespDTO.builder()
                                    .date(date)
                                    .pv(0)
                                    .uv(0)
                                    .uip(0)
                                    .build();
                            daily.add(linkStatsAccessDailyRespDTO);
                        }
                ));

        // 周每天基础访问信息
        List<Integer> weekdayStats = new ArrayList<>(7);
        for (int i = 0; i < 7; i++) {
            weekdayStats.add(0);
        }
        linkAccessStatusMapper.selectWeekdayList(requireParam)
                .forEach(linkStatusDO -> weekdayStats.set(linkStatusDO.getAccessWeekday() - 1, linkStatusDO.getPv()));

        // 小时基础访问信息
        List<Integer> hourStats = new ArrayList<>(24);
        for (int i = 0; i < 24; i++) {
            hourStats.add(0);
        }
        linkAccessStatusMapper.selectHourList(requireParam)
                .forEach(linkStatusDO -> hourStats.set(linkStatusDO.getAccessHour(), linkStatusDO.getPv()));

        // 地区访问信息
        List<LinkStatsLocaleCNRespDTO> localeCnStats;
        List<LinkLocaleStatusDO> linkLocaleStatusList = linkLocaleStatusMapper.selectLocaleCnList(requireParam);
        int localeTotalCnt = linkLocaleStatusList.stream()
                .mapToInt(LinkLocaleStatusDO::getAccessCount)
                .sum();
        localeCnStats = linkLocaleStatusList.stream()
                .map(linkLocaleStatusDO -> {
                    double ratio = (double) linkLocaleStatusDO.getAccessCount() / localeTotalCnt;
                    ratio = Math.round(ratio * 100) / 100.0;
                    return LinkStatsLocaleCNRespDTO.builder()
                            .locale(linkLocaleStatusDO.getProvince())
                            .cnt(linkLocaleStatusDO.getAccessCount())
                            .ratio(ratio)
                            .build();
                }).toList();

        // 高频 IP 访问信息
        List<Map<String, Object>> linkAccessTopIpList = linkAccessLogMapper.selectTopIpList(requireParam);
        List<LinkStatsTopIpRespDTO> topIpStats = linkAccessTopIpList.stream()
                .map(linkAccessTopIpMap -> LinkStatsTopIpRespDTO.builder()
                        .ip(linkAccessTopIpMap.get("access_ip").toString())
                        .cnt(Integer.parseInt(linkAccessTopIpMap.get("cnt").toString()))
                        .build())
                .toList();

        // 访问浏览器信息
        List<LinkBrowserStatusDO> linkBrowserStatusList = linkBrowserStatusMapper.selectBrowserList(requireParam);
        int browserTotalCnt = linkBrowserStatusList.stream()
                .mapToInt(LinkBrowserStatusDO::getAccessCount)
                .sum();
        List<LinkStatsBrowserRespDTO> browserStats = linkBrowserStatusList.stream()
                .map(linkBrowserStatusDO -> {
                    double ratio = (double) linkBrowserStatusDO.getAccessCount() / browserTotalCnt;
                    ratio = Math.round(ratio * 100) / 100.0;
                    return LinkStatsBrowserRespDTO.builder()
                            .browser(linkBrowserStatusDO.getBrowser())
                            .cnt(linkBrowserStatusDO.getAccessCount())
                            .ratio(ratio)
                            .build();
                }).toList();

        // 访问操作系统信息
        List<LinkOsStatusDO> linkOsStatusList = linkOsStatusMapper.selectOsList(requireParam);
        int osTotalCnt = linkOsStatusList.stream()
                .mapToInt(LinkOsStatusDO::getAccessCount)
                .sum();
        List<LinkStatsOsRespDTO> osStats = linkOsStatusList.stream()
                .map(linkOsStatusDO -> {
                    double ratio = (double) linkOsStatusDO.getAccessCount() / osTotalCnt;
                    ratio = Math.round(ratio * 100) / 100.0;
                    return LinkStatsOsRespDTO.builder()
                            .os(linkOsStatusDO.getOperatorSystem())
                            .cnt(linkOsStatusDO.getAccessCount())
                            .ratio(ratio)
                            .build();
                }).toList();

        // 访客类型信息
        List<Map<String, Object>> linkAccessUvTypeList = linkAccessLogMapper.selectUvTypeList(requireParam);
        int uvTypeTotalCnt = linkAccessUvTypeList.stream()
                .mapToInt(map -> Integer.parseInt(map.get("cnt").toString()))
                .sum();
        List<LinkStatsUvRespDTO> uvTypeStats = linkAccessUvTypeList.stream()
                .map(map -> {
                    int cnt = Integer.parseInt(map.get("cnt").toString());
                    double ratio = (double) cnt / uvTypeTotalCnt;
                    ratio = Math.round(ratio * 100) / 100.0;
                    return LinkStatsUvRespDTO.builder()
                            .uvType((String) map.get("uvType"))
                            .cnt(cnt)
                            .ratio(ratio)
                            .build();
                }).toList();

        // 设备访问信息
        List<LinkDeviceStatusDO> linkDeviceStatusList = linkDeviceStatusMapper.selectDeviceList(requireParam);
        int deviceTotalCnt = linkDeviceStatusList.stream()
                .mapToInt(LinkDeviceStatusDO::getAccessCount)
                .sum();
        List<LinkStatsDeviceRespDTO> deviceStats = linkDeviceStatusList.stream()
                .map(linkDeviceStatusDO -> {
                    double ratio = (double) linkDeviceStatusDO.getAccessCount() / deviceTotalCnt;
                    ratio = Math.round(ratio * 100) / 100.0;
                    return LinkStatsDeviceRespDTO.builder()
                            .device(linkDeviceStatusDO.getDevice())
                            .cnt(linkDeviceStatusDO.getAccessCount())
                            .ratio(ratio)
                            .build();
                }).toList();

        // 访问使用网络信息
        List<LinkNetworkStatusDO> linkNetworkStatusList = linkNetworkStatusMapper.selectNetworkList(requireParam);
        int networkTotalCnt = linkNetworkStatusList.stream()
                .mapToInt(LinkNetworkStatusDO::getAccessCount)
                .sum();
        List<LinkStatsNetworkRespDTO> networkStats = linkNetworkStatusList.stream()
                .map(linkNetworkStatusDO -> {
                    double ratio = (double) linkNetworkStatusDO.getAccessCount() / networkTotalCnt;
                    ratio = Math.round(ratio * 100) / 100.0;
                    return LinkStatsNetworkRespDTO.builder()
                            .network(linkNetworkStatusDO.getNetwork())
                            .cnt(linkNetworkStatusDO.getAccessCount())
                            .ratio(ratio)
                            .build();
                }).toList();

        return LinkStatusRespDTO.builder()
                .pv(pv)
                .uv(uv)
                .uip(uip)
                .weekdayStats(weekdayStats)
                .hourStats(hourStats)
                .daily(daily)
                .localeCnStats(localeCnStats)
                .topIpStats(topIpStats)
                .browserStats(browserStats)
                .osStats(osStats)
                .uvTypeStats(uvTypeStats)
                .deviceStats(deviceStats)
                .networkStats(networkStats)
                .build();
    }

    @Override
    public IPage<LinkAccessRecordRespDTO> selectLinkAccessRecord(LinkAccessRecordReqDTO linkAccessRecordReqDTO) {
        IPage<LinkAccessLogDO> linkAccessPage = linkAccessLogMapper.selectPageAccessRecord(linkAccessRecordReqDTO);
        return linkAccessPage.convert(linkAccessLogDO -> LinkAccessRecordRespDTO.builder()
                .user(linkAccessLogDO.getUser())
                .ip(linkAccessLogDO.getAccessIp())
                .locale(linkAccessLogDO.getAccessLocale())
                .os(linkAccessLogDO.getAccessOs())
                .browser(linkAccessLogDO.getAccessBrowser())
                .device(linkAccessLogDO.getAccessDevice())
                .network(linkAccessLogDO.getAccessNetwork())
                .uvType(linkAccessLogDO.getUvType())
                .createTime(linkAccessLogDO.getCreateTime())
                .build());
    }

    @Override
    public GroupLinkStatusRespDTO groupLinkStatus(GroupLinkStatusReqDTO requireParam) {
        // 基础访问信息
        LinkAccessStatusDO linkAccessPvUvUip = linkAccessLogMapper.selectGroupTotalPvUvUip(requireParam);
        Integer pv = linkAccessPvUvUip.getPv();
        Integer uv = linkAccessPvUvUip.getUv();
        Integer uip = linkAccessPvUvUip.getUip();
        if (pv == null || pv.equals(0)) {
            return null;
        }

        // 每日基础访问信息
        List<LinkAccessStatusDO> linkStatsDailyList = linkAccessStatusMapper.selectGroupDailyList(requireParam);
        List<String> rangeDateList = DateUtil.rangeToList(DateUtil.parse(requireParam.getStartDate()), DateUtil.parse(requireParam.getEndDate()), DateField.DAY_OF_MONTH)
                .stream()
                .map(DateUtil::formatDate)
                .toList();
        List<LinkStatsAccessDailyRespDTO> daily = new ArrayList<>(rangeDateList.size());
        rangeDateList.forEach(date -> linkStatsDailyList.stream()
                .filter(linkStatusDO -> CharSequenceUtil.equals(linkStatusDO.getAccessDate().toString(), date))
                .findFirst()
                .ifPresentOrElse(linkStatusDO -> {
                            LinkStatsAccessDailyRespDTO linkStatsAccessDailyRespDTO = LinkStatsAccessDailyRespDTO.builder()
                                    .date(date)
                                    .pv(linkStatusDO.getPv())
                                    .uv(linkStatusDO.getUv())
                                    .uip(linkStatusDO.getUip())
                                    .build();
                            daily.add(linkStatsAccessDailyRespDTO);
                        }, () -> {
                            LinkStatsAccessDailyRespDTO linkStatsAccessDailyRespDTO = LinkStatsAccessDailyRespDTO.builder()
                                    .date(date)
                                    .pv(0)
                                    .uv(0)
                                    .uip(0)
                                    .build();
                            daily.add(linkStatsAccessDailyRespDTO);
                        }
                ));

        // 周每天基础访问信息
        List<Integer> weekdayStats = new ArrayList<>(7);
        for (int i = 0; i < 7; i++) {
            weekdayStats.add(0);
        }
        linkAccessStatusMapper.selectGroupWeekdayList(requireParam)
                .forEach(linkStatusDO -> weekdayStats.set(linkStatusDO.getAccessWeekday() - 1, linkStatusDO.getPv()));

        // 小时基础访问信息
        List<Integer> hourStats = new ArrayList<>(24);
        for (int i = 0; i < 24; i++) {
            hourStats.add(0);
        }
        linkAccessStatusMapper.selectGroupHourList(requireParam)
                .forEach(linkStatusDO -> hourStats.set(linkStatusDO.getAccessHour(), linkStatusDO.getPv()));

        // 地区访问信息
        List<LinkStatsLocaleCNRespDTO> localeCnStats;
        List<LinkLocaleStatusDO> linkLocaleStatusList = linkLocaleStatusMapper.selectGroupLocaleCnList(requireParam);
        int localeTotalCnt = linkLocaleStatusList.stream()
                .mapToInt(LinkLocaleStatusDO::getAccessCount)
                .sum();
        localeCnStats = linkLocaleStatusList.stream()
                .map(linkLocaleStatusDO -> {
                    double ratio = (double) linkLocaleStatusDO.getAccessCount() / localeTotalCnt;
                    ratio = Math.round(ratio * 100) / 100.0;
                    return LinkStatsLocaleCNRespDTO.builder()
                            .locale(linkLocaleStatusDO.getProvince())
                            .cnt(linkLocaleStatusDO.getAccessCount())
                            .ratio(ratio)
                            .build();
                }).toList();

        // 高频 IP 访问信息
        List<Map<String, Object>> linkAccessTopIpList = linkAccessLogMapper.selectGroupTopIpList(requireParam);
        List<LinkStatsTopIpRespDTO> topIpStats = linkAccessTopIpList.stream()
                .map(linkAccessTopIpMap -> LinkStatsTopIpRespDTO.builder()
                        .ip(linkAccessTopIpMap.get("access_ip").toString())
                        .cnt(Integer.parseInt(linkAccessTopIpMap.get("cnt").toString()))
                        .build())
                .toList();

        // 访问浏览器信息
        List<LinkBrowserStatusDO> linkBrowserStatusList = linkBrowserStatusMapper.selectGroupBrowserList(requireParam);
        int browserTotalCnt = linkBrowserStatusList.stream()
                .mapToInt(LinkBrowserStatusDO::getAccessCount)
                .sum();
        List<LinkStatsBrowserRespDTO> browserStats = linkBrowserStatusList.stream()
                .map(linkBrowserStatusDO -> {
                    double ratio = (double) linkBrowserStatusDO.getAccessCount() / browserTotalCnt;
                    ratio = Math.round(ratio * 100) / 100.0;
                    return LinkStatsBrowserRespDTO.builder()
                            .browser(linkBrowserStatusDO.getBrowser())
                            .cnt(linkBrowserStatusDO.getAccessCount())
                            .ratio(ratio)
                            .build();
                }).toList();

        // 访问操作系统信息
        List<LinkOsStatusDO> linkOsStatusList = linkOsStatusMapper.selectGroupOsList(requireParam);
        int osTotalCnt = linkOsStatusList.stream()
                .mapToInt(LinkOsStatusDO::getAccessCount)
                .sum();
        List<LinkStatsOsRespDTO> osStats = linkOsStatusList.stream()
                .map(linkOsStatusDO -> {
                    double ratio = (double) linkOsStatusDO.getAccessCount() / osTotalCnt;
                    ratio = Math.round(ratio * 100) / 100.0;
                    return LinkStatsOsRespDTO.builder()
                            .os(linkOsStatusDO.getOperatorSystem())
                            .cnt(linkOsStatusDO.getAccessCount())
                            .ratio(ratio)
                            .build();
                }).toList();

        // 访客类型信息
        List<Map<String, Object>> linkAccessUvTypeList = linkAccessLogMapper.selectGroupUvTypeList(requireParam);
        int uvTypeTotalCnt = linkAccessUvTypeList.stream()
                .mapToInt(map -> Integer.parseInt(map.get("cnt").toString()))
                .sum();
        List<LinkStatsUvRespDTO> uvTypeStats = linkAccessUvTypeList.stream()
                .map(map -> {
                    int cnt = Integer.parseInt(map.get("cnt").toString());
                    double ratio = (double) cnt / uvTypeTotalCnt;
                    ratio = Math.round(ratio * 100) / 100.0;
                    return LinkStatsUvRespDTO.builder()
                            .uvType((String) map.get("uvType"))
                            .cnt(cnt)
                            .ratio(ratio)
                            .build();
                }).toList();

        // 设备访问信息
        List<LinkDeviceStatusDO> linkDeviceStatusList = linkDeviceStatusMapper.selectGroupDeviceList(requireParam);
        int deviceTotalCnt = linkDeviceStatusList.stream()
                .mapToInt(LinkDeviceStatusDO::getAccessCount)
                .sum();
        List<LinkStatsDeviceRespDTO> deviceStats = linkDeviceStatusList.stream()
                .map(linkDeviceStatusDO -> {
                    double ratio = (double) linkDeviceStatusDO.getAccessCount() / deviceTotalCnt;
                    ratio = Math.round(ratio * 100) / 100.0;
                    return LinkStatsDeviceRespDTO.builder()
                            .device(linkDeviceStatusDO.getDevice())
                            .cnt(linkDeviceStatusDO.getAccessCount())
                            .ratio(ratio)
                            .build();
                }).toList();

        // 访问使用网络信息
        List<LinkNetworkStatusDO> linkNetworkStatusList = linkNetworkStatusMapper.selectGroupNetworkList(requireParam);
        int networkTotalCnt = linkNetworkStatusList.stream()
                .mapToInt(LinkNetworkStatusDO::getAccessCount)
                .sum();
        List<LinkStatsNetworkRespDTO> networkStats = linkNetworkStatusList.stream()
                .map(linkNetworkStatusDO -> {
                    double ratio = (double) linkNetworkStatusDO.getAccessCount() / networkTotalCnt;
                    ratio = Math.round(ratio * 100) / 100.0;
                    return LinkStatsNetworkRespDTO.builder()
                            .network(linkNetworkStatusDO.getNetwork())
                            .cnt(linkNetworkStatusDO.getAccessCount())
                            .ratio(ratio)
                            .build();
                }).toList();

        return GroupLinkStatusRespDTO.builder()
                .pv(pv)
                .uv(uv)
                .uip(uip)
                .weekdayStats(weekdayStats)
                .hourStats(hourStats)
                .daily(daily)
                .localeCnStats(localeCnStats)
                .topIpStats(topIpStats)
                .browserStats(browserStats)
                .osStats(osStats)
                .uvTypeStats(uvTypeStats)
                .deviceStats(deviceStats)
                .networkStats(networkStats)
                .build();
    }

    @Override
    public IPage<GroupLinkAccessRecordRespDTO> selectGroupLinkAccessRecord(GroupLinkAccessRecordReqDTO groupLinkAccessRecordReqDTO) {
        IPage<LinkAccessLogDO> linkGroupAccessPage = linkAccessLogMapper.selectPageGroupAccessRecord(groupLinkAccessRecordReqDTO);
        return linkGroupAccessPage.convert(linkAccessLogDO -> GroupLinkAccessRecordRespDTO.builder()
                .user(linkAccessLogDO.getUser())
                .ip(linkAccessLogDO.getAccessIp())
                .locale(linkAccessLogDO.getAccessLocale())
                .os(linkAccessLogDO.getAccessOs())
                .browser(linkAccessLogDO.getAccessBrowser())
                .device(linkAccessLogDO.getAccessDevice())
                .network(linkAccessLogDO.getAccessNetwork())
                .uvType(linkAccessLogDO.getUvType())
                .createTime(linkAccessLogDO.getCreateTime())
                .build());
    }
}
